#!/usr/bin/env php
<?php

define('MSGID_HEADER', 'msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8\n"

');

define('PROGRAM', basename(array_shift($argv)));
define('TMPDIR', sys_get_temp_dir());

/**
 *
 *
 * demo:
 * 扫描整个目录下全部模块下的po
 * php ProcessPo.php /home/neeke/www/jiankongbao/monitor/www/templates
 *
 * 扫描整个目录下abc与edf两个模块的po
 * php ProcessPo.php -d abc,edf /home/neeke/www/jiankongbao/monitor/www/templates
 *
 * 扫描同时进行翻译   接受 en cht
 * php ProcessPo.php -d abc,edf -t en /home/neeke/www/jiankongbao/monitor/www/templates
 *
 * @author neeke@php.net
 *
 * Class ProcessPo
 */
class ProcessPo
{
    const IS_TPL = 1;
    const IS_PHP = 2;

    private $ldq;
    private $rdq;
    private $cmd;
    private $extensionsTemplate = array();
    private $extensionsPhp = array('php');
    private $aLanguage = array('en', 'cht'); //英文 繁体中文

    private $extensionsAll = array();

    private $argv;
    private $sOutfilePath = './po/{domain}.po';

    private $sDomainDefault = 'common';

    private $sDomainFileter = NULL;

    private $sLanguage = NULL;

    private $aTransCache = array();

    public function __construct($argv)
    {
        if ('cli' != php_sapi_name()) {
            error_log("ERROR: This program is for command line mode only.");
            exit(1);
        }

        $this->ldq = preg_quote('{');

        $this->rdq = preg_quote('}');

        $this->cmd = preg_quote('t');

        $this->argv = $argv;


        $opt = getopt('d:t:');

        if ($opt) {
            if (array_key_exists('d', $opt)) {
                $tmp                  = $opt['d'];
                $this->sDomainFileter = explode(',', $tmp);
            }

            if (array_key_exists('t', $opt) && in_array($opt['t'], $this->aLanguage)) {
                $this->sLanguage = $opt['t'];
            }
        }

        $this->extensionsAll = array_merge($this->extensionsTemplate, $this->extensionsPhp);
    }

    /**
     * @param $str
     *
     * @return mixed|string
     */
    private function fs($str)
    {
        $str = stripslashes($str);
        $str = str_replace('"', '\"', $str);
        $str = str_replace("\n", '\n', $str);

        return $str;
    }

    /**
     * @param $content
     * @param $offset
     *
     * @return int
     */
    private function lineno_from_offset($content, $offset)
    {
        return substr_count($content, "\n", 0, $offset) + 1;
    }

    /**
     * @param $domain
     *
     * @return mixed
     */
    private function processFileName($domain)
    {
        return str_replace('{domain}', $domain, $this->sOutfilePath);
    }

    /**
     * @param $domain
     * @param $data
     */
    private function msgmerge($domain, $data)
    {
        // skip empty
        if (empty($data) || $data == MSGID_HEADER) {
            return;
        }

        $sPoFile = $this->processFileName($domain);
        if (!file_exists($sPoFile)) {
            file_put_contents($sPoFile, MSGID_HEADER);
        }

        // write new data to tmp file
        $tmp = tempnam(TMPDIR, $domain);
        file_put_contents($tmp, $data);

        // temp file for result cat
        $tmp2 = tempnam(TMPDIR, $domain);

        passthru('msgcat -o ' . escapeshellarg($tmp2) . ' ' . escapeshellarg($sPoFile) . ' ' . escapeshellarg($tmp), $rc);
        unlink($tmp);

        if ($rc) {
            fwrite(STDERR, "msgcat failed with $rc\n");
            exit($rc);
        }

        // rename if output was produced
        if (file_exists($tmp2)) {
            $result = copy($tmp2, $sPoFile);
            if ($result) {
                unlink($tmp2);
            }
        }
    }

    /**
     * @param $file
     * @param $isTplOrPhp
     */
    private function do_file($file, $isTplOrPhp)
    {
        $content = file_get_contents($file);

        if (empty($content)) {
            return;
        }

        $domain = $this->sDomainDefault;

        $msgids        = array();
        $msgids_plural = array();

        if ($isTplOrPhp == self::IS_TPL) {
            preg_match_all("/{$this->ldq}\s*({$this->cmd})\s*([^{$this->rdq}]*){$this->rdq}+([^{$this->ldq}]*){$this->ldq}\/\\1{$this->rdq}/", $content, $matches, PREG_OFFSET_CAPTURE
            );

            for ($i = 0; $i < count($matches[0]); $i++) {
                $domainRaw = $matches[2][$i][0];
                if ($domainRaw) {
                    $matchCount = preg_match_all('/d=["|\'](.*?)["|\']/', $domainRaw, $domainMatch);
                    if ($matchCount > 0) {
                        $domain = $domainMatch[1][0];
                    }
                }

                if (empty($domain)) $domain = $this->sDomainDefault;

                if (preg_match('/plural\s*=\s*["\']?\s*(.[^\"\']*)\s*["\']?/', $matches[2][$i][0], $match)) {
                    $msgid                          = $matches[3][$i][0];
                    $msgids_plural[$domain][$msgid] = $match[1];
                } else {
                    $msgid = $matches[3][$i][0];
                }

                if (is_array($this->sDomainFileter) && !in_array($domain, $this->sDomainFileter)) {
                    continue;
                }

                $lineno                    = $this->lineno_from_offset($content, $matches[2][$i][1]);
                $msgids[$domain][$msgid][] = $file . ':' . $lineno;
            }
        } elseif ($isTplOrPhp == self::IS_PHP) {
            preg_match_all('/[,| |.|>|\(]L\((.*?)\)/', $content, $matches, PREG_OFFSET_CAPTURE);

            foreach ($matches[1] as $match) {
                $aMatch = explode(',', $match[0]);
                if (count($aMatch) > 1) {
                    $aReplace                  = array('\'' => '');
                    $domain                    = strtr($aMatch[0], $aReplace);
                    $msgid                     = strtr($aMatch[1], $aReplace);
                    $lineno                    = $this->lineno_from_offset($content, $match[1]);
                    $msgids[$domain][$msgid][] = $file . ':' . $lineno;
                }
            }
        }

        foreach ($msgids as $domain => $msgidsTmp) {
            ob_start();
            echo MSGID_HEADER;
            foreach ($msgidsTmp as $msgid => $files) {
                echo "#: ", join(' ', $files), "\n";
                if (isset($msgids_plural[$domain][$msgid])) {
                    echo 'msgid "' . $this->fs($msgid) . '"', "\n";
                    echo 'msgid_plural "' . $this->fs($msgids_plural[$domain][$msgid]) . '"', "\n";
                    echo 'msgstr[0] ""', "\n";
                    echo 'msgstr[1] ""', "\n";
                } else {
                    echo 'msgid "' . $this->fs($msgid) . '"', "\n";

                    $str = NULL;
                    if ($this->sLanguage) {
                        $str = $this->transWithBaidu($msgid, $this->sLanguage);
                    }

                    if ($str) {
                        echo 'msgstr "' . $str . '"', "\n";
                    } else {
                        echo 'msgstr ""', "\n";
                    }
                }
                echo "\n";
            }

            $out = ob_get_contents();
            ob_end_clean();
            $this->msgmerge($domain, $out);
        }
    }

    /**
     * @param $dir
     */
    private function do_dir($dir)
    {
        $d = dir($dir);

        while (FALSE !== ($entry = $d->read())) {
            if ($entry == '.' || $entry == '..') {
                continue;
            }

            $entry = $dir . '/' . $entry;

            if (is_dir($entry)) { // if a directory, go through it
                $this->do_dir($entry);
            } else { // if file, parse only if extension is matched
                $pi = pathinfo($entry);
                if (isset($pi['extension']) && in_array($pi['extension'], $this->extensionsAll)) {
                    if (in_array($pi['extension'], $this->extensionsTemplate)) {
                        $isTplOrPhp = self::IS_TPL;
                    } elseif (in_array($pi['extension'], $this->extensionsPhp)) {
                        $isTplOrPhp = self::IS_PHP;
                    }
                    $this->do_file($entry, $isTplOrPhp);
                }
            }
        }

        $d->close();
    }

    /**
     * @param        $str
     * @param        $to
     * @param string $from
     *
     * @return bool
     */
    private function transWithBaidu($str, $to, $from = 'zh')
    {
        $sKey = md5($str . $to . $from);
        if (array_key_exists($sKey, $this->aTransCache)) {
            return $this->aTransCache[$sKey];
        }


        $data = array('from' => $from, 'to' => $to, 'query' => $str, 'transtype' => 'trans', 'simple_means_flag' => 3);
        $url  = "http://fanyi.baidu.com/v2transapi";
        $ch   = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        $output = json_decode(curl_exec($ch), TRUE);
        curl_close($ch);

        if (is_array($output) && count($output) > 1 && array_key_exists('trans_result', $output) && array_key_exists('data', $output['trans_result'])) {
            if (isset($output['trans_result']['data'][0]['dst'])) {
                $result                   = $output['trans_result']['data'][0]['dst'];
                $this->aTransCache[$sKey] = $result;

                return $result;
            }
        }

        return FALSE;
    }

    /**
     *
     */
    public function run()
    {
        // remove -o FILENAME from $argv.
        if (isset($opt['o'])) {
            foreach ($this->argv as $i => $v) {
                if ($v != '-o') {
                    continue;
                }

                unset($this->argv[$i]);
                unset($this->argv[$i + 1]);
                break;
            }
        }

        // process dirs/files
        foreach ($this->argv as $arg) {
            if (is_dir($arg)) {
                $this->do_dir($arg);
            } else {
                if (file_exists($arg)) {
                    $pi = pathinfo($arg);
                    if (isset($pi['extension']) && in_array($pi['extension'], $this->extensionsAll)) {
                        if (in_array($pi['extension'], $this->extensionsTemplate)) {
                            $isTplOrPhp = self::IS_TPL;
                        } elseif (in_array($pi['extension'], $this->extensionsPhp)) {
                            $isTplOrPhp = self::IS_PHP;
                        }
                        $this->do_file($arg, $isTplOrPhp);
                    }
                }
            }
        }

    }
}

$oBuild = new ProcessPo($argv);
$oBuild->run();